package org.jtsmix.mixfile;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * stores properties of a file contained in mix archive
 * <p/>
 * Raw data are stored in little-endian format as folllowing
 * <p/>
 * [4B] - crc id counted from filename [4B] - file offset (excludes body offset
 * - call MixFileHeader.getDataOffset) [4B] - size in bytes
 *
 * @author ivosh-mint
 */
public class MixFileIndexEntry {

    private long crcId;
    private long offset = 0;
    private long size = 0;

    public long getCrcId() {
        return crcId;
    }

    public long getOffset() {
        return offset;
    }

    public void setOffset(long offset) {
        this.offset = offset;
    }

    public long getSize() {
        return size;
    }

    /**
     * create empty file header
     *
     * @param crcId crc id
     */
    public MixFileIndexEntry(long crcId) {
        this.crcId = crcId;
    }

    /**
     * create file header by parameters
     *
     * @param crcId  crc id
     * @param offset offset in mix archive body (excludes body offset - call
     *               MixFileHeader.getDataOffset)
     * @param size   size in bytes
     */
    public MixFileIndexEntry(long crcId, long offset, long size) {
        this.crcId = crcId;
        this.offset = offset;
        this.size = size;
    }

    /**
     * write entry to stream
     * <p/>
     * Converts header to raw data (see class documentation) and writes it to
     * stream.
     *
     * @param stream
     * @throws IOException
     */
    public void writeEntry(OutputStream stream)
            throws IOException {
        byte buffer[] = new byte[12];
        for (int i = 0; i < 4; i++) {
            buffer[i] = (byte) ((this.crcId & (0xFF << (i * 8))) >>> (i * 8));
            buffer[i + 4] = (byte) ((this.offset & (0xFF << (i * 8))) >>> (i * 8));
            buffer[i + 8] = (byte) ((this.size & (0xFF << (i * 8))) >>> (i * 8));
        }

        stream.write(buffer);
    }

    /**
     * construct header from raw data read from stream
     * <p/>
     * Raw data must be in little-endian and follow the specification. See
     * documentation.
     *
     * @param is inputstream.
     * @throws IOException
     */
    public MixFileIndexEntry(InputStream is)
            throws IOException {
        DataInputStream stream = new DataInputStream(is);
        int buffer[] = new int[12];

        for (int i = 0; i < 12; i++) {
            buffer[i] = stream.readUnsignedByte();
        }

        this.crcId = buffer[0] + ((long) buffer[1] << 8) + ((long) buffer[2] << 16) + ((long) buffer[3] << 24);
        this.offset = buffer[4] + ((long) buffer[5] << 8) + ((long) buffer[6] << 16) + ((long) buffer[7] << 24);
        this.size = buffer[8] + ((long) buffer[9] << 8) + ((long) buffer[10] << 16) + ((long) buffer[11] << 24);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final MixFileIndexEntry other = (MixFileIndexEntry) obj;
        if (this.crcId != other.crcId) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        return (int) this.crcId;
    }
}